//== SAGITTARIUS ============================================================================

//== BEGINNING OF CODE ===============================================================

#include "sagittarius/core/SagittariusCore.h"

//SAGITTARIUS headers:
#include "sagittarius/parallel/multisema.h"
#include "sagittarius/parallel/atomics.h"

#include "sagittarius/io/io.h"

#if SAGITTARIUS_PLATFORM == SAGITTARIUS_PLATFORM_LINUX
    #define __USE_GNU
    #include <linux/futex.h>
    #include <sys/time.h>
    #include <sys/syscall.h>
    #include <limits.h>
    #include "sagittarius/platform/syscall.h"

    void initsema(pmultisema f) {
        *f = 0;
    }
    
    void wlsema(pmultisema f) {
        multisema ff;
        while(1) {
            ff = *f;
            if(!(ff&0x40000000)) {
                if(sg_cas32(f, ff, ff|0x40000000))
                    break; //successfully acquired writer lock
                continue; //try again
            }
            //another writer already holds the lock
            //wait
            if(sg_cas32((uint32*)f, ff, ff|0x80000000)) //used to use ff |= 0x80000000
                syscall(SYS_futex, f, FUTEX_WAIT, ff|0x80000000, NULL);
        }
        //wait for readers to finish
        ff = *f;
        while(ff & 0x1FFFFFFF) {
            syscall(SYS_futex, f, FUTEX_WAIT, ff, NULL);
            ff = *f;
        }
    }

    void wusema(pmultisema f) {
        multisema ff = *f;
        AZ(ff & 0x1FFFFFFF); //Invalid state
        *f = 0x00000000;
        if(ff & 0xE0000000)
            syscall(SYS_futex, f, FUTEX_WAKE, INT_MAX);
    }

    void rlsema(pmultisema f) {
        multisema ff;
        while(1) {
            ff = *f;
            if(ff&0xC0000000) {
                //writer lock
                //indicate that there are readers waiting
                if(sg_cas32((uint32*)f, ff, ff|0x20000000)) //ff = ff|0x20000000
                    syscall(SYS_futex, f, FUTEX_WAIT, ff|0x20000000, NULL); //wait for it
                continue;
            }
            //no writers
            AZ(ff & 0x20000000); //Invalid state
            if(sg_cas32((uint32*)f, ff, ff+1))
                break; //successfully acquired reader lock
        }
    }

    void rusema(pmultisema f) {
        multisema ff;
        while(1) {
            ff = *f;
            if(ff & 0xC0000000) {
                //writers are waiting
                if(sg_cas32((uint32*)f, ff, ff-1)) {
                    if(!((ff-1) & 0x1FFFFFFF)) {
                        //last reader to finish
                        syscall(SYS_futex, f, FUTEX_WAKE, INT_MAX); //wake writers
                    }
                    break;
                } else continue;
            }
            //no writers waiting
            if(sg_cas32((uint32*)f, ff, ff-1))
                break;
        }
    }
#elif SAGITTARIUS_PLATFORM == SAGITTARIUS_PLATFORM_WIN
    #if SAGITTARIUS_WIN_MINVER >= SAGITTARIUS_WIN_MINVER_WIN7
    #if 0
        void initsema(pmultisema f) {
            InitializeSRWLock(f);
        }
        
        void wlsema(pmultisema f) {
            AcquireSRWLockExclusive(f);
        }
        
        void wusema(pmultisema f) {
            ReleaseSRWLockExclusive(f);
        }
        
        void rlsema(pmultisema f) {
            AcquireSRWLockShared(f);
        }
        
        void rusema(pmultisema f) {
            ReleaseSRWLockShared(f);
        }
    #endif
    #else
        #error "Windows slim locks are not supported on this version of Windows. Consider implementing DXUTLockFreePipe (see msdn.microsoft.com/en-us/library/windows/desktop/aa904937%28v=vs.85%29.aspx)"
    #endif
#else
    #error "No thread support"
#endif